package com.uhuibao.tmall.apis.controller;


import cn.hutool.core.util.StrUtil;
import com.uhuibao.tmall.api.domain.response.ResponseResult;
import com.uhuibao.tmall.api.domain.vo.OrderChargeRequest;
import com.uhuibao.tmall.api.domain.vo.OrderQueryRequest;
import com.uhuibao.tmall.api.service.IOrderService;
import com.uhuibao.tmall.common.core.controller.BaseController;
import com.uhuibao.tmall.common.utils.MD5;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;


/**
 * 天猫充值服务 Controller
 *
 * @author haungzj
 * @date 2023-09-11
 */
@RestController
public class ApiController {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private IOrderService orderService;

    @Value("${tmall.tmallSecret}")
    private String tmallSecret;

    /**
     * 充值接口
     *
     * @return
     */
    @GetMapping(value = "/charge")
    public ResponseEntity<String> charge(@Validated OrderChargeRequest request) throws IOException, NoSuchAlgorithmException {

        Long tbOrderNo = request.getTbOrderNo();
        String bizType = request.getBizType();

        //验签
        TreeMap<String, Object> allParams = convertToTreeMap(request);
        Map<String, String> verifySign = verifySign(allParams);
        if (verifySign != null) {
            String errCode = verifySign.get("errCode");
            String errDesc = verifySign.get("errDesc");
            return xmlResponse(tbOrderNo, bizType, null, null, errCode, errDesc);
        }


        //业务逻辑
        Map<String, Object> orderDeal = orderService.orderDeal(request);
        String ccopOrderStatus = orderDeal.get("coopOrderStatus") == null ? null : orderDeal.get("coopOrderStatus").toString();
        String coopOrderNo = orderDeal.get("coopOrderNo") == null ? null : orderDeal.get("coopOrderNo").toString();
        String errCode = orderDeal.get("errCode") == null ? null : orderDeal.get("errCode").toString();
        String errDesc = orderDeal.get("errDesc") == null ? null : orderDeal.get("errDesc").toString();

        if (errCode.equals("SUCCESS") || errCode.equals("UNDERWAY")) {
            errCode = null;
            errDesc = null;
        }

        //最终成功返回
        return xmlResponse(tbOrderNo, bizType, ccopOrderStatus, coopOrderNo, errCode, errDesc);
    }

    /**
     * 查询接口
     *
     * @return
     */
    @GetMapping(value = "/query", produces = MediaType.APPLICATION_XML_VALUE)
    public ResponseEntity<String> query(@Validated OrderQueryRequest request) throws UnsupportedEncodingException, NoSuchAlgorithmException {

        Long tbOrderNo = request.getTbOrderNo();
        String bizType = request.getBizType();
        //验签
        TreeMap<String, Object> allParams = convertToTreeMap(request);
        Map<String, String> verifySign = verifySign(allParams);
        if (verifySign != null) {
            String errCode = verifySign.get("errCode");
            String errDesc = verifySign.get("errDesc");
            return xmlResponse(tbOrderNo, bizType, null, null, errCode, errDesc);
        }


        //判断订单是否已经存在，存在了 就直接返回订单状态
        Map<String, Object> orderInfo = orderService.getChainTidyUpOrderInfo(request.getTbOrderNo().toString(), null);
        if (orderInfo == null) {
            String errCode = "ORDER_NOT_EXISTS";
            String errDesc = "订单不存在！";
            return xmlResponse(tbOrderNo, bizType, null, null, errCode, errDesc);
        }

        String coopOrderNo = orderInfo.get("coopOrderNo") == null ? null : orderInfo.get("coopOrderNo").toString();

        String errCode = orderInfo.get("errCode") == null ? null : orderInfo.get("errCode").toString();
        String errDesc = orderInfo.get("errDesc") == null ? null : orderInfo.get("errDesc").toString();

        String ccopOrderStatus = errCode;
        if (errCode.equals("SUCCESS") || errCode.equals("UNDERWAY")) {
            errCode = null;
            errDesc = null;
        }

        //最终成功返回
        return xmlResponse(tbOrderNo, bizType, ccopOrderStatus, coopOrderNo, errCode, errDesc);
    }

    /**
     * 返回
     *
     * @param tbOrderNo
     * @param bizType
     * @param ccopOrderNo
     * @param errCode
     * @param errDesc
     * @return
     */
    public ResponseEntity<String> xmlResponse(Long tbOrderNo, String bizType, String ccopOrderStatus, String ccopOrderNo, String errCode, String errDesc) {
        ResponseResult responseResult = new ResponseResult();

        responseResult.setTbOrderNo(tbOrderNo);
        responseResult.setBizType(bizType);
        responseResult.setCoopOrderStatus(ccopOrderStatus);
        if (ccopOrderNo != null) {
            responseResult.setCoopOrderNo(ccopOrderNo);
        }
        if (errCode != null) {
            responseResult.setErrCode(errCode);
            responseResult.setErrDesc(errDesc);
        }
        String xmlResponse = convertObjectToXml(responseResult);
        return ResponseEntity.ok().contentType(MediaType.valueOf(MediaType.APPLICATION_XML + ";charset=GBK"))
                .body(xmlResponse);
    }

    protected String convertObjectToXml(Object object) {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(object.getClass());
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "GBK");
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);

            StringWriter stringWriter = new StringWriter();
            stringWriter.write("<?xml version=\"1.0\" encoding=\"GBK\"?>\n");
            marshaller.marshal(object, stringWriter);

            return stringWriter.toString();
        } catch (JAXBException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 签名验签
     *
     * @param allParams
     */
    protected Map<String, String> verifySign(TreeMap<String, Object> allParams) throws UnsupportedEncodingException, NoSuchAlgorithmException {

        String ts = allParams.get("ts").toString();
        logger.info("【ts】：{}", ts);
        logger.info("【dq】：{}", System.currentTimeMillis());
        //比较时间，3分钟内为合法请求
        if (System.currentTimeMillis() - Math.abs(Long.parseLong(ts)) > 180000) {
            Map<String, String> retMap = new HashMap<>();
            retMap.put("errCode", "TS_EXPIRED");
            retMap.put("errDesc", "ts请求已过期");
            return retMap;
        }

        // 对方签名
        String sign = allParams.get("sign").toString();
        allParams.remove("sign");
        // 拼接参数字符串
        StringBuilder stringBuilder = new StringBuilder();
        for (Map.Entry<String, Object> entry : allParams.entrySet()) {
            stringBuilder.append(entry.getKey()).append(entry.getValue());
        }
        // 打印拼接后的参数字符串
        String concatenatedParams = stringBuilder.toString() + tmallSecret;
        logger.info("【拼接后的参数字符串】：{} .", concatenatedParams);
        String mySign = MD5.encryptMD5(concatenatedParams);

        logger.info("【验签，对方签名】：{}", sign);
        logger.info("【验签，我方签名】：{}", mySign);
        if (!StrUtil.equals(sign, mySign)) {
            Map<String, String> retMap = new HashMap<>();
            retMap.put("errCode", "INVALID_SIGN");
            retMap.put("errDesc", "验签失败（签名错误）");
            return retMap;
        }
        return null;
    }


    private static TreeMap<String, Object> convertToTreeMap(Object object) {
        TreeMap<String, Object> paramMap = new TreeMap<>();

        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String fieldName = field.getName();
            Object fieldValue;
            try {
                fieldValue = field.get(object);
            } catch (IllegalAccessException e) {
                fieldValue = null;
            }
            paramMap.put(fieldName, fieldValue);
        }

        return paramMap;
    }
}
